/*
 * Copyright 2010 Michael Nitsche
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.mnit.basis.db.sql;

import de.mnit.basis.daten.ArrayHilfe;
import de.mnit.basis.daten.FormatText;
import de.mnit.basis.daten.WandleText;
import de.mnit.basis.daten.struktur.S_Folge;
import de.mnit.basis.daten.struktur.container.A_Kistenstapel;
import de.mnit.basis.daten.struktur.container.DBKiste;
import de.mnit.basis.daten.struktur.container.S_Kiste;
import de.mnit.basis.daten.struktur.gruppe.Gruppe2;
import de.mnit.basis.daten.struktur.liste.S_Liste;
import de.mnit.basis.daten.struktur.schluessel.S_SchluesselMenge;
import de.mnit.basis.daten.struktur.tabelle.S_Tabelle;
import de.mnit.basis.daten.typ.zeit.S_Datum;
import de.mnit.basis.daten.typ.zeit.S_Zeitpunkt;
import de.mnit.basis.db.konstant.DBMS;
import de.mnit.basis.fehler.Fehler;
import de.mnit.basis.sys.Ausgabe;

/**
 * @author Michael Nitsche
 * 29.01.2009	Erstellt
 */
public class SqlHilfe {

	public static String kisteZuSQLUpdate(A_Kistenstapel kisten, S_Kiste<?>... nicht) {
		return iKisteZuSQL(kisten, nicht, true, true);
	}

	public static String kisteZuSQLSpalten(A_Kistenstapel kisten, S_Kiste<?>... nicht) {
		return iKisteZuSQL(kisten, nicht, true, false);
	}

	public static String kisteZuSQLWerte(A_Kistenstapel kisten, S_Kiste<?>... nicht) {
		return iKisteZuSQL(kisten, nicht, false, true);
	}

	/**
	 * Als Parameter dürfen Kisten und Strings übergeben werden.
	 * FIXME: TESTEN!!!!!!!!
	 **/
	public static String zuSQLSpalten(Object... kisten) {
		Fehler.bereich.wenn_ElementklasseFalsch(kisten, String.class, DBKiste.class);
		StringBuilder sb=new StringBuilder();
		boolean erster=true;
		for(Object o : kisten) {
			if(!erster)
				sb.append(',');
			erster=false;
			if(o instanceof String)
				sb.append(""+o);
			else
				sb.append(((DBKiste<?>)o).gName().toLowerCase());
		}

		return sb.toString();
	}

	/**
	 * Als Parameter dürfen Kisten, Strings und Zahlen übergeben werden.
	 * FIXME: TESTEN!!!!!!!!
	 **/
	public static String zuSQLWerten(Object... kisten) {
		Fehler.bereich.wenn_ElementklasseFalsch(kisten, String.class, DBKiste.class, Integer.class, Long.class);
		StringBuilder sb=new StringBuilder();
		boolean erster=true;
		for(Object o : kisten) {
			if(!erster)
				sb.append(',');
			erster=false;
			if(o == null)
				sb.append("NULL"); //TODO Testen
			else if(o instanceof String)
				sb.append(FormatText.zuDB_Text(""+o));
			else if(o instanceof Integer || o instanceof Long)
				sb.append(""+o);
			else
				sb.append(FormatText.zuDB_Text(((DBKiste<?>)o).g()+""));
		}
		return sb.toString();
	}

	public static void kisteSetzen(A_Kistenstapel kisten, S_Liste<String> dbz, S_Liste<String> felder) {
		S_SchluesselMenge<String, S_Kiste<?>> k=kisten.gKisten();

		int gesetzt=0;
		for(int pos=1; pos<=felder.gLaenge(); pos++) {
			String feld=felder.gib(pos);
			S_Kiste<?> kiste=k.gib(feld.toUpperCase());
			if(kiste==null) {
				Ausgabe.debug("Wert wurde nicht gesetzt!", feld);
				continue;
			}
//			kiste.s(Wandle.text.zuObject(dbz.gib(pos), kiste.g));
			iSetze(kiste, dbz.gib(pos));
			gesetzt++;
		}
		if(felder.gLaenge()!=gesetzt)
			Ausgabe.debug("Es wurden nicht alle Felder gesetzt!!!");
	}

	public static String suche(String suche, DBKiste<?>... spalten) {
		return suche(1, suche, spalten);
	}

	public static String suche(int such_nr, String suche, DBKiste<?>... spalten) {
		String[] spalten2=new String[spalten.length];
		for(int i=0; i<spalten.length; i++)
			spalten2[i]=spalten[i].gName();
		return suche(such_nr, suche, spalten2);
	}

	public static String suche(String suche, String... spalten) {
		return suche(1, suche, spalten);
	}

	public static String suche(int such_nr, String suche, String... spalten) {
		Fehler.zahl.wenn_ZuKlein(1, such_nr);
		Fehler.zahl.wenn_ZuKlein(1, spalten.length);
		if(suche==null)
			return "";
		StringBuilder sb=new StringBuilder();
		sb.append(such_nr==1 ? " WHERE " : " AND ");
		sb.append("(UPPER("+spalten[0]+") LIKE UPPER('%"+suche+"%')"); //Die Klammer ist zur besseren Weiterverarbeitung!
		for(int p=1; p<spalten.length; p++)
			sb.append(" OR UPPER("+spalten[p]+") LIKE UPPER('%"+suche+"%')");
		sb.append(')');
		return sb.toString();
	}

	public static String suche(S_Tabelle<String> spalte_suche) {
		Fehler.zahl.wenn_ZuKlein(1, spalte_suche.gLaenge());
		Fehler.zahl.wenn_ZuKlein(2, spalte_suche.gBreite());
		StringBuilder sb=new StringBuilder();
		boolean erster=true;
		for(S_Folge<String> z : spalte_suche) {
			if(z.gib(2)==null || z.gib(2).length()==0)
				continue;
			sb.append(erster ? " WHERE" : " AND");
			sb.append(" UPPER("+z.gib(1)+") LIKE UPPER('%"+z.gib(2)+"%')");
			erster=false;
		}
		return sb.toString();
	}

	public static String whereAnd(S_Folge<String> where) {
		return iWhere(where, "AND");
	}

	public static String whereOr(S_Folge<String> where) {
		return iWhere(where, "OR");
	}

	public static String zuGleich(String wert) {
		return wert==null
			? " IS NULL"
			: "="+FormatText.zuDB_Text(wert);
	}

	public static String zuGleich(Integer wert) {
		return wert==null
			? " IS NULL"
			: "="+wert;
	}

	/**
	 * Von DBKisten wird der Feldname mit IFNULL umschlossen.
	 * Strings werden direkt integriert (Flexibel)
	 */
	public static String zuWennNull(Object... felder) {
		boolean erster=true;
		StringBuilder sb=new StringBuilder();
		for(Object o : felder) {
			Fehler.objekt.wenn_Null(o); //Erstmal! Ändern wenn nötig!
			sb.append(erster ? "" : ",");
			if(o instanceof DBKiste<?>) {
				DBKiste<?> k=(DBKiste<?>)o;
				if(k.gPflicht())
					sb.append(k.gName());
				else {
					if(k.gTyp()==String.class)
						sb.append("IFNULL("+k.gName()+",'')");
					else if(k.gTyp()==Integer.class)
						sb.append("IFNULL(CAST("+k.gName()+" AS VARCHAR),'')");
					else Fehler.sonstige.da_ToDo(k.gName(), k.gTyp());
				}

			}
			else if(o instanceof String)
				sb.append(""+o);
			else Fehler.sonstige.da_ToDo("Unbekannte Klasse", o.getClass());
			erster=false;
		}
		return sb.toString();
	}

	public static String letzteID(DBMS dbms) {
		switch(dbms) {
			case HSQLDB: return "IDENTITY()";
			case MYSQL:  return "LAST_INSERT_ID()";
		}
		throw Fehler.sonstige.da_ToDo();
	}

	// INTERN

	private static String iWhere(S_Folge<String> where, String trenner2x) {
		boolean erster=true;
		trenner2x=" "+trenner2x+" "; //Trenner ab dem 2. Datensatz
		StringBuilder sb=new StringBuilder();
		for(String s : where) {
			if(s!=null && s.length()>0) {
				sb.append(erster ? " WHERE " : trenner2x);
				sb.append(s);
				erster=false;
			}
		}
		return sb.toString();
	}

	private static <T> void iSetze(S_Kiste<T> kiste, String wert) {
		@SuppressWarnings("unchecked")
		T t=WandleText.zuObject(wert, (Class<T>) (kiste.g()!=null ? kiste.g().getClass() : Object.class));
		kiste.s(t);
	}

	private static String iKisteZuSQL(A_Kistenstapel kisten, S_Kiste<?>[] nicht, boolean felder, boolean wert) {
		S_SchluesselMenge<String,S_Kiste<?>> k=kisten.gKisten();

		StringBuilder sb=new StringBuilder();
		boolean erster=true;
		for(Gruppe2<String, S_Kiste<?>> gk : k) {
			if(ArrayHilfe.kennt(nicht, gk.g2()))
					continue;
			if(!erster)
				sb.append(',');
			erster=false;
			if(felder)
				sb.append(gk.g1().toLowerCase());
			if(felder && wert)
				sb.append("=");
			if(wert)
				sb.append(zuTextSQL( gk.g2().g() ));
		}

		return sb.toString();
	}

	public static String sGenerator(DBMS dbms, String tabelle, int wert) {
		switch(dbms) {
			case HSQLDB: return "ALTER TABLE "+tabelle+" ALTER COLUMN ID RESTART WITH "+wert;
			case MYSQL:  return "ALTER TABLE "+tabelle+" AUTO_INCREMENT="+wert;
		}
		throw Fehler.sonstige.da_ToDo();
	}

	public static String zuTextSQL(Object o) {
		if(o==null)
			return "NULL";
		if(o instanceof String)
			return ""+FormatText.zuDB_Text((String)o)+"";
		if(o instanceof S_Datum)
			return "'"+((S_Datum)o).zuTextEDV()+"'";
		if(o instanceof S_Zeitpunkt)
			return "'"+((S_Zeitpunkt)o).zuTextEDV()+"'";
		if(o instanceof Integer)
			return ""+((Integer)o);
		if(o instanceof Boolean)
			return ((Boolean)o) ? "TRUE" : "FALSE";
		throw Fehler.sonstige.da_ToDo("Klasse noch nicht aufgenommen: "+o.getClass());
	}

}
